package com.heima.wemedia.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.common.constants.message.NewsUpOrDownConstants;
import com.heima.common.constants.message.PublishArticleConstants;
import com.heima.common.constants.wemedia.NewsAutoScanConstants;
import com.heima.common.constants.wemedia.WemediaConstants;
import com.heima.common.excpetion.CustException;
import com.heima.file.service.FileStorageService;
import com.heima.model.common.dtos.PageResponseResult;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.media.dtos.WmNewsDTO;
import com.heima.model.threadlocal.WmThreadLocalUtils;
import com.heima.model.wemedia.dtos.NewsAuthDTO;
import com.heima.model.wemedia.dtos.WmNewsPageReqDTO;
import com.heima.model.wemedia.pojos.WmNews;
import com.heima.model.wemedia.pojos.WmNewsMaterial;
import com.heima.model.wemedia.pojos.WmUser;
import com.heima.model.wemedia.vos.WmNewsVO;
import com.heima.wemedia.mapper.WemediaUseMapper;
import com.heima.wemedia.mapper.WmMaterialMapper;
import com.heima.wemedia.mapper.WmNewsAuditMapper;
import com.heima.wemedia.mapper.WmNewsMaterialMapper;
import com.heima.wemedia.service.WmNewsService;
import io.seata.common.util.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 自媒体文章处理类
 */
@Service("WmNewsService")
public class WmNewsServiceImpl extends ServiceImpl<WmNewsAuditMapper, WmNews> implements WmNewsService {
    @Value("${file.oss.web-site}")
    private String webSite;

    @Value("${file.oss.prefix}")
    String prefix;

    @Autowired
    FileStorageService fileStorageService;

    /**
     * 分页带条件查询自媒体文章列表
     *
     * @param dto
     * @return
     */
    @Override
    public ResponseResult findList(WmNewsPageReqDTO dto) {
        if (dto == null)
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);

        //检查线程中是否有User 否返回
        WmUser user = WmThreadLocalUtils.getUser();
        if (user == null)
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);

        LambdaQueryWrapper<WmNews> lambdaQuery = Wrappers.lambdaQuery();

        if (dto.getKeyword() != null)
            lambdaQuery.eq(WmNews::getTitle, dto.getKeyword());

        if (dto.getChannelId() != null)
            lambdaQuery.eq(WmNews::getChannelId, dto.getChannelId());

        if (dto.getStatus() != null)
            lambdaQuery.eq(WmNews::getStatus, dto.getStatus());

        //初始时间不为NUll
        lambdaQuery.ge(dto.getBeginPubDate() != null, WmNews::getPublishTime, dto.getBeginPubDate());
        //结束时间不为null
        lambdaQuery.le(dto.getEndPubDate() != null, WmNews::getPublishTime, dto.getEndPubDate());

        // 根据当前登录Id查询
        lambdaQuery.eq(WmNews::getUserId, user.getId());

        //根据创建时间进行排序
        lambdaQuery.orderByDesc(WmNews::getCreatedTime);

        // 准备分页查询
        Page<WmNews> newsPage = new Page<>(dto.getPage(), dto.getSize());
        // 得到查询结果
        IPage<WmNews> pageResult = super.page(newsPage, lambdaQuery);


        PageResponseResult result = new PageResponseResult(dto.getPage(), dto.getSize(), pageResult.getTotal());
        result.setData(pageResult.getRecords());
        // 处理文章图片
        result.setHost(webSite);
        return result;
    }

    @Autowired
    RabbitTemplate rabbitTemplate;

    /**
     * 自媒体文章发布
     *
     * @param dto
     * @return
     */
    @Override
    public ResponseResult submitNews(WmNewsDTO dto) {
        // 1 参数校验
        if (StringUtils.isBlank(dto.getContent()))
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID);

        //检查线程中是否有User 否返回
        WmUser user = WmThreadLocalUtils.getUser();
        if (user == null)
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);

        //创建文章对象
        WmNews wmNews = new WmNews();
        BeanUtils.copyProperties(dto, wmNews);

        // 如果为自动图片 则设置为null
        if (wmNews.getType() == -1)
            wmNews.setType(null);

        // 处理dto参数 images封面集合 转换成 字符串
        List<String> dtoImages = dto.getImages();
        String images = this.imageListToStr(dtoImages);

        wmNews.setImages(images);
        //保存文章
        this.saveWmNews(wmNews);

        /*
          抽取文章中关联的图片路径
         */

        //如果是草稿 直接返回
        if (dto.getStatus() == 0)
            return ResponseResult.okResult();

        String content = dto.getContent();
        List<HashMap> hashMaps = JSON.parseArray(content, HashMap.class);

        // 获取所有的图片的路径
        List<String> imageList = parseContentImages(hashMaps);

        // 关联文章内容中的图片和素材关系

        if (CollectionUtils.isNotEmpty(imageList)) {
            saveRelativeInfo(imageList, wmNews.getId(), WemediaConstants.WM_CONTENT_REFERENCE);
        }


        //关联文章封面中的图片和素材关系  封面可能是选择自动或者是无图
        this.saveRelativeInfoForCover(imageList, wmNews, dto);

        // 发送自动审核信息
        rabbitTemplate.convertAndSend(NewsAutoScanConstants.WM_NEWS_AUTO_SCAN_QUEUE, wmNews.getId());

        return ResponseResult.okResult();
    }

    /**
     * 编辑时根据id查询 自媒体文章
     *
     * @param id 文章Id
     * @return 查询到的文章
     */
    @Override
    public ResponseResult findWmNewsById(Integer id) {
        if (id == null)
            CustException.cust(AppHttpCodeEnum.PARAM_REQUIRE);

        WmNews wmNews = super.getById(id);
        //当数据库 没有该数据 或者 数据处于发布状态 不允许修改
        if (wmNews == null)
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID);

        wmNews.setImages(strToImages(wmNews.getImages()));
        return ResponseResult.okResult(wmNews);
    }

    /**
     * 根据文章Id删除文章
     *
     * @param id 文章Id
     * @return 删除结果
     */
    @Override
    public ResponseResult delNews(Integer id) {
        //1.检查参数
        if (id == null)
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID);

        //检查线程中是否有User 否返回
        WmUser user = WmThreadLocalUtils.getUser();
        if (user == null)
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);

        //查询要删除的文章数据
        WmNews wmNews = super.getById(id);
        if (wmNews == null)
            CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST, "文章不存在");

        // 判断当前文章的状态 如果为已发布(status=9)并且已上架(enable=1) 不允许删除
        if (wmNews.getStatus().equals(WmNews.Status.PUBLISHED.getCode())
                && wmNews.getEnable().equals(WemediaConstants.WM_NEWS_UP)) {
            CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST, "文章已发布，不能删除");
        }
        // 删除素材与文章的关系
        wmNewsMaterialMapper.delete(Wrappers.<WmNewsMaterial>lambdaQuery().eq(WmNewsMaterial::getNewsId, wmNews.getId()));

        // 删除文章
        removeById(wmNews.getId());
        return ResponseResult.okResult();
    }

    /**
     * 文章上下架
     *
     * @param dto
     * @return
     */
    @Override
    public ResponseResult downOrUp(WmNewsDTO dto) {
        if (dto == null)
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);

        //检查线程中是否有User 否返回
        WmUser user = WmThreadLocalUtils.getUser();
        if (user == null)
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);

        WmNews wmNews = super.getById(dto.getId());
        if (wmNews == null)
            CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST, "文章不存在");

        //执行修改
        LambdaUpdateWrapper<WmNews> lambdaUpdate = Wrappers.lambdaUpdate();
        lambdaUpdate.eq(WmNews::getId, dto.getId()).set(WmNews::getEnable, dto.getEnable());
        super.update(lambdaUpdate);

        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

    @Autowired
    WmNewsAuditMapper wmNewsAuditMapper;

    /**
     * 查询待审核文章列表
     *
     * @param dto 文章认证对象
     * @return
     */
    @Override
    public ResponseResult finNewsList(NewsAuthDTO dto) {
        if (dto == null)
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);

        dto.checkParam();
        //获取当前页
        Integer page = dto.getPage();
        Integer size = dto.getSize();

        //根据页面大小设置当前页
        dto.setPage((page - 1) * size);

        //分页查询待审核文章
        List<WmNewsVO> listAndPage = wmNewsAuditMapper.findListAndPage(dto);

        //获取待审核文章列表大小
        long listCount = wmNewsAuditMapper.findListCount(dto);

        //构建分页返回对象
        PageResponseResult responseResult = new PageResponseResult(page, size, listCount, listAndPage);
        responseResult.setHost(webSite);

        return responseResult;
    }

    @Autowired
    WemediaUseMapper wemediaUseMapper;

    /**
     * 查询待审核文章详情
     *
     * @param id 文章Id
     * @return
     */
    @Override
    public ResponseResult findWmNewsVo(Integer id) {
        if (id == null)
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);

        WmNews wmNews = super.getById(id);

        //数据库中不存在该文章
        if (wmNews == null)
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);

        //根据作者Id查询作者信息
        WmUser wmUser = wemediaUseMapper.selectById(wmNews.getUserId());

        //创建返回文章前端对象
        WmNewsVO wmNewsVO = new WmNewsVO();
        BeanUtils.copyProperties(wmNews, wmNewsVO);

        //添加作者姓名
        wmNewsVO.setAuthorName(wmUser.getName());

        ResponseResult responseResult = ResponseResult.okResult(wmNewsVO);
        responseResult.setHost(webSite);

        return responseResult;
    }

    /**
     * 自媒体文章人工审核
     *
     * @param status 2  审核失败  4 审核成功
     * @param dto
     * @return
     */
    @Override
    public ResponseResult updateStatus(Short status, NewsAuthDTO dto) {
        if (status == null || dto == null || dto.getId() == null)
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);

        WmNews wmNews = super.getById(dto.getId());

        if (wmNews == null)
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);

        // 如果修改的状态不为待审核或待人工审核 不允许审核
        if (!wmNews.getStatus().equals(WmNews.Status.SUBMIT.getCode()) && !wmNews.getStatus().equals(WmNews.Status.ADMIN_AUTH.getCode()))
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);

        wmNews.setStatus(status);

        if (StringUtils.isNotBlank(dto.getMsg()))
            wmNews.setReason(dto.getMsg());

        super.updateById(wmNews);

        //状态允许准备向MQ发送发布消息
        if (wmNews.getStatus() == WmNews.Status.ADMIN_SUCCESS.getCode() || wmNews.getStatus() == WmNews.Status.SUCCESS.getCode())
            pushNews(wmNews, rabbitTemplate);

        //如果文章的详细信息Id不为空 则通过MQ通知文章 进行上下架操作
        if (wmNews.getArticleId() != null) {
            if (wmNews.getEnable().equals(WemediaConstants.WM_NEWS_UP)) {
                // 上架消息
                rabbitTemplate.convertAndSend(
                        NewsUpOrDownConstants.NEWS_UP_OR_DOWN_EXCHANGE,
                        NewsUpOrDownConstants.NEWS_UP_ROUTE_KEY,
                        wmNews.getArticleId());
            } else {
                // 下架消息
                rabbitTemplate.convertAndSend(
                        NewsUpOrDownConstants.NEWS_UP_OR_DOWN_EXCHANGE,
                        NewsUpOrDownConstants.NEWS_DOWN_ROUTE_KEY,
                        wmNews.getArticleId());
            }
        }

        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

    /**
     * 用于推送文章 包括延时文章
     *
     * @param wmNews
     * @param rabbitTemplate
     */
    static void pushNews(WmNews wmNews, RabbitTemplate rabbitTemplate) {
        long publishTime = wmNews.getPublishTime().getTime();
        long nowTime = System.currentTimeMillis();
        rabbitTemplate.convertAndSend(
                PublishArticleConstants.DELAY_DIRECT_EXCHANGE,
                PublishArticleConstants.PUBLISH_ARTICLE_ROUTE_KEY,
                wmNews.getId(),
                message -> {
                    long remainTime = publishTime - nowTime;
                    message.getMessageProperties().setHeader("x-delay"
                            , remainTime <= 0 ? 0 : remainTime);
                    return message;
                }
        );
    }

    /**
     * 关联文章封面中的图片和素材关系
     *
     * @param imageList
     * @param wmNews
     */
    private void saveRelativeInfoForCover(List<String> imageList, WmNews wmNews, WmNewsDTO dto) {
        //前端传来的封面集合 (有前缀)
        List<String> images = dto.getImages();

        //判断封面是否为-1 -> -1为自动 -> 需要生成封面
        if (dto.getType().equals(WemediaConstants.WM_NEWS_TYPE_AUTO)) {

            //假如用户没有上传图片
            if (CollectionUtils.isEmpty(imageList))
                // 设置封面为无图
                wmNews.setType(WemediaConstants.WM_NEWS_NONE_IMAGE);
            else if (imageList.size() > 0 && imageList.size() < 3) {
                //设置类型为双图模式
                wmNews.setType(WemediaConstants.WM_NEWS_SINGLE_IMAGE);
                // 覆盖images设置封面为第一张
                images = imageList.stream().limit(1).collect(Collectors.toList());
            } else {
                //设置类型为多图模式
                wmNews.setType(WemediaConstants.WM_NEWS_MANY_IMAGE);
                images = imageList.stream().limit(3).collect(Collectors.toList());
            }
            // 如果图片集合不为空 则转成字符串
            if (CollectionUtils.isNotEmpty(images)) {
                wmNews.setImages(imageListToStr(images));
            }
            // 修改wmNews
            super.updateById(wmNews);
        }
        // 如果图片集合不为空 则像数据库存入图片和素材关系
        if (CollectionUtils.isNotEmpty(images)) {
            images = images.stream().
                    map(i -> i.replaceAll(webSite, ""))
                    .distinct().
                    collect(Collectors.toList());

            this.saveRelativeInfo(images, wmNews.getId(), WemediaConstants.WM_IMAGE_REFERENCE);
        }

    }

    @Autowired
    WmMaterialMapper wmMaterialMapper;

    /**
     * 关联文章内容中的图片和素材关系
     *
     * @param imageUrls 素材路径集合
     * @param id
     * @param type
     */
    private void saveRelativeInfo(List<String> imageUrls, Integer id, Short type) {
        Integer userId = WmThreadLocalUtils.getUser().getId();

        // 根据素材资源路径，查询相关素材id
        List<Integer> ids = wmMaterialMapper.selectRelationsIds(imageUrls, userId);

        //检查id的数量和url的大小 小于则素材丢失 抛出异常
        if (ids.size() < imageUrls.size()) {
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID, "素材丢失");
        }
        // 批量保存关联关系
        wmNewsMaterialMapper.saveRelations(ids, id, type);

    }

    /**
     * 将前端传来的图片URL批量转为数据库存储格式
     *
     * @param hashMaps
     * @return
     */
    private List<String> parseContentImages(List<HashMap> hashMaps) {
        return hashMaps.stream().
                //取出type的value 拿type=image的数据
                        filter(mp -> mp.get("type").equals(WemediaConstants.WM_NEWS_TYPE_IMAGE)).
                //取出image的value
                        map(o -> o.get("value").toString()).
                map(u -> u.replaceAll(webSite, "")).
                //去重
                        distinct().
                //收集数据
                        collect(Collectors.toList());
    }

    /**
     * 图片列表转字符串，并去除图片前缀
     *
     * @param images 图片列表
     */
    private String imageListToStr(List<String> images) {
        return images.stream()  // 获取流
                .map((url) -> url.replace(webSite, ""))  // 对流数据的中间操作
                .collect(Collectors.joining(","));
    }

    /**
     * 字符串转图片集合，加上图片前缀
     *
     * @param target 数据库中图片url字符串
     */
    private String strToImages(String target) {
        return Arrays.stream(target.split(",")).map(s -> webSite + s).collect(Collectors.joining(","));
    }

    @Autowired
    WmNewsMaterialMapper wmNewsMaterialMapper;

    /**
     * 保存或修改文章
     *
     * @param wmNews 文章对象（前端传递）
     */
    private void saveWmNews(WmNews wmNews) {
        wmNews.setCreatedTime(new Date());
        wmNews.setUserId(WmThreadLocalUtils.getUser().getId());
        wmNews.setSubmitedTime(new Date());
        wmNews.setEnable(WemediaConstants.WM_NEWS_UP); // 上架
        if (wmNews.getId() == null) { // 数据库中没有数据==>保存操作
            super.save(wmNews);
        } else {  // 修改
            // 当前文章 和 素材关系表数据删除
            wmNewsMaterialMapper.delete(Wrappers.<WmNewsMaterial>lambdaQuery()
                    .eq(WmNewsMaterial::getNewsId, wmNews.getId()));
            super.updateById(wmNews);
        }
    }

}
